package com.hoshino.momochi.service.Impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableList;
import com.hoshino.momochi.model.DTO.MessageDTO;
import com.hoshino.momochi.model.domain.Blog;
import com.hoshino.momochi.service.BlogService;
import com.hoshino.momochi.service.RedisService;
import com.hoshino.momochi.util.LuaScript;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Service
@Slf4j
public class RedisServiceImpl implements RedisService {

    private final String LOGINMAP = "login_map";

    private final String MAILLATE = "600";

    @Autowired
    private BlogService blogService;

    private final RedisTemplate<String, Serializable> limitRedisTemplate;

    private final StringRedisTemplate stringRedisTemplate;

    @Autowired
    public RedisServiceImpl(RedisTemplate<String, Serializable> limitRedisTemplate, StringRedisTemplate stringRedisTemplate) {
        this.limitRedisTemplate = limitRedisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public String getCacheBlog(String blogFront, int need_page) {
        // redis
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join(blogFront, need_page));
        String luaScript = LuaScript.getCacheScript();
        RedisScript<String> redisScript = new DefaultRedisScript<>(luaScript, String.class);

        // 执行 lua 脚本
        String count = stringRedisTemplate.execute(redisScript, keys); // keys ARGV...
        return count;
    }

    @Override
    public void CacheBlogPage(int pages, List<Blog> blogList, int pageInfo, String blogFront) {
        // 分页过后的 每页 JSON 字符串
        List<String> Ans = new ArrayList<>();

        for (int i = 0; i < pages; i++) {
            Ans.add(JSON.toJSONString(blogList.stream().skip(i * pageInfo).limit(pageInfo).toList()));
        }

        String luaScriptBlog = LuaScript.doCacheFrontScript(blogFront);
        RedisScript<Void> redisScriptBlog = new DefaultRedisScript<>(luaScriptBlog, Void.class);

        // 执行 lua 脚本
        stringRedisTemplate.execute(redisScriptBlog, Ans, StringUtils.join(pages), blogFront); // keys ARGV...
    }

    @Override
    public void CacheBlog(List<Blog> blogList) {
        ImmutableList<String> keys = ImmutableList.of("cache_all_blog");
        String luaScriptBlog = LuaScript.cacheBlogScript();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScriptBlog, Void.class);

        stringRedisTemplate.execute(redisScript, keys, JSON.toJSONString(blogList));
    }

    @Override
    public String getCacheBlogN() {
        ImmutableList<String> keys = ImmutableList.of("cache_all_blog");
        String luaScriptBlog = LuaScript.getCacheBlogScript();
        RedisScript<String> redisScript = new DefaultRedisScript<>(luaScriptBlog, String.class);

        return stringRedisTemplate.execute(redisScript, keys);
    }

    @Override
    public Number LimitLogin(String prefix, String key, String ip, int limitCount, int limitPeriod) {
        // KEYS
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join(prefix + "_", key, ip));
        String luaScript = LuaScript.buildLuaScript();
        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
        Number count = limitRedisTemplate.execute(redisScript, keys, limitCount, limitPeriod); // keys ARGV...
        return count;
    }

    @Override
    public Number MaintainToken(String token, Long id, int latetime) {
        // KEYS
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join(token));
        String luaScript = LuaScript.setTokenAndReturnLuaScript();
        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);

        // run
        // 执行 lua 脚本
        Number count = limitRedisTemplate.execute(redisScript, keys, id, latetime); // keys ARGV...
        return count;
    }

    @Override
    public void MapUserIdToToken(Long uid, String token, int latetime) {
        ImmutableList<String> keys = ImmutableList.of(LOGINMAP);
        String luaScript = LuaScript.mapUserIdToTokenScript();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScript, Void.class);

        stringRedisTemplate.execute(redisScript, keys, StringUtils.join(uid), token, StringUtils.join(latetime));
    }

    @Override
    public void CacheUsername(List<String> keys, String userIndex) {
        String limitLen = StringUtils.join(keys.size());
        //String userIndex = "usernames";
        // KEYS
        String luaScript = LuaScript.cacheUsernameScript();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScript, Void.class);
        // 执行 lua 脚本
        stringRedisTemplate.execute(redisScript, keys/*value列表*/, limitLen/*for循环次数*/, userIndex/*key*/); // keys ARGV...
    }

    @Override
    public Number CheckUsername(String username) {
        // 查询用户名是否已经存在
        ImmutableList<String> keys = ImmutableList.of("usernames");
        // KEYS
        String luaScript = LuaScript.checkUsernameScript();
        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
        Number count = stringRedisTemplate.execute(redisScript, keys/*key*/, username); // keys ARGV...
        return count;
    }

    @Override
    public void AddUsername(String username) {
        // 新增用户名 保存redis
        ImmutableList<String> value = ImmutableList.of(username);
        // KEYS
        String luaScript2 = LuaScript.cacheUsernameScript();
        RedisScript<Void> redisScript2 = new DefaultRedisScript<>(luaScript2, Void.class);

        // 执行 lua 脚本
        stringRedisTemplate.execute(redisScript2, value/*key*/, StringUtils.join(1), "usernames"); // keys ARGV...
    }

    @Override
    public void RevokeUsername(String token) {
        ImmutableList<String> keys = ImmutableList.of(token);
        // KEYS
        String luaScript = LuaScript.deleteScript();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScript, Void.class);

        // 执行 lua 脚本
        stringRedisTemplate.execute(redisScript, keys/*token*/);
    }

    @Override
    public void MessageCache(Long pullId, MessageDTO messageDTO) {
        // redis
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join("message_", pullId));
        String luaScript = LuaScript.saveMessageScript();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScript, Void.class);

        // 执行 lua 脚本
        stringRedisTemplate.execute(redisScript, keys/*message_id*/, JSON.toJSONString(messageDTO)); // keys ARGV...
    }

    @Override
    public List<String> PullMessage(Long id) {
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join("message_", id));
        String luaScript = LuaScript.pullMessageScript();
        RedisScript<List> redisScript = new DefaultRedisScript<>(luaScript, List.class);
        log.info("ready pull message");
        // 执行
        return stringRedisTemplate.execute(redisScript, keys);
    }

    @Override
    public void BlogUpdateCache() {
        log.info("blog del cache begin");
        List<Blog> blogList = blogService.findAllBriefBlogs();
        Collections.sort(blogList, (a, b) -> {
            if (a.getCreateTime().after(b.getCreateTime())) {
                return -1;
            } else {
                return 1;
            }
        });
        this.CacheBlog(blogList); // 不分页
        log.info("blog cache end");
    }

    @Override
    public void DelUserLoginSta(String token) {
        log.info("修改密码，重新登录");
        ImmutableList<String> keys = ImmutableList.of(token);
        String luaScript = LuaScript.delUserLoginStaScript();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScript, Void.class);

        stringRedisTemplate.execute(redisScript, keys);
    }

    @Override
    @Async
    public void addMailToCode(String mail, String code) {
//        log.info("------------redis-begin------------");
        ImmutableList<String> keys = ImmutableList.of(mail);
        String luaScriptBlog = LuaScript.addMailToCode();
        RedisScript<Void> redisScript = new DefaultRedisScript<>(luaScriptBlog, Void.class);

        stringRedisTemplate.execute(redisScript, keys, code, MAILLATE);
//        log.info("------------redis-end------------");
    }

    @Override
    public String getMailCode(String mail) {
        ImmutableList<String> keys = ImmutableList.of(mail);
        String luaScriptBlog = LuaScript.getMailCode();
        RedisScript<String> redisScript = new DefaultRedisScript<>(luaScriptBlog, String.class);
        return stringRedisTemplate.execute(redisScript, keys);
    }
}
